//file:include/os/rwlock.h
//autor:jiangxinpeng
//time:2021.4.12
//copyright:(C) 2020-2050 by jiangxinpeng,All right are reserved.

#ifndef OS_RWLOCK_H
    #define OS_RWLOCK_H

    #include<os/mutexlock.h>

    typedef enum rwlock_arg
    {
     RWLOCK_RD_FIRST=0,   //read first  
     RWLOCK_WR_FIRST,  //write first
     RWLOCK_RW_FAIR,   //read/write fair
    }rwlock_arg_t;

    typedef struct rwlock rwlock_t;

    typedef struct rwlock
    {   
        int count;
        mutexlock_t count_mutex; //update count metex
        mutexlock_t rw_mutex;    //read/write mutex
        mutexlock_t write_mutex; //write first
        void (*read_lock)(rwlock_t *);
        void (*write_lock)(rwlock_t *);
        void (*read_unlock)(rwlock_t *);
        void (*write_unlock)(rwlock_t *);
    }rwlock_t;

    #define RWLOCK_INIT_RD_FIRST(lockname)  \
    {   .count=0,   \
        .count_mutex=MUTEX_LOCK_INIT((lockname).count_lock), \
        .rw_mutex=MUTWX_LOCK_INIT((lockname).rw_mutex), \
        .write_mutex=MutexlockInit((lockname).write_mutex), \
        .read_lock=_RwLockReadLockRdFirst,  \
        .read_unlock=_RwLockReadUnLockRdFirst,   \
        .write_lock=_RwLockWriteLockRdFirst,    \
        .write_unlock=_RwLockWriteUnLockRdFirst,    \
    }

    #define RWLOCk_INIT_WR_FIRST(lockname) \
     {   .count=0,   \
        .count_mutex=MUTEX_LOCK_INIT((lockname).count_lock), \
        .rw_mutex=MUTWX_LOCK_INIT((lockname).rw_mutex), \
        .write_mutex=MutexlockInit((lockname).write_mutex), \
        .read_lock=_RwLockReadLockWrFirst,  \
        .read_unlock=_RwLockReadUnLockWrFirst,   \
        .write_lock=_RwLockWriteLockWrFirst,    \
        .write_unlock=_RwLockWriteUnLockWrFirst,    \
    }

      #define RWLOCk_INIT_RW_FAIR(lockname) \
     {   .count=0,   \
        .count_mutex=MUTEX_LOCK_INIT((lockname).count_lock), \
        .rw_mutex=MUTWX_LOCK_INIT((lockname).rw_mutex), \
        .write_mutex=MutexlockInit((lockname).write_mutex), \
        .read_lock=_RwLockReadLockRDFair,  \
        .read_unlock=_RwLockReadUnLockRDFair,   \
        .write_lock=_RwLockWriteLockRDFair,    \
        .write_unlock=_RwLockWriteUnLockRDFair,    \
    }

    #define DEFINE_RWLOCK_RD_FIRST(lockname) \
        rwlock_t lockname=RWLOCK_INIT_RD_FIRST(lockname)
    #define DEFINE_RWLOCK_WR_FIRST(lockname) \
        rwlock_t lockname=RWLOCk_INIT_WR_FIRST(lockname)
    #define DEFINE_RWLOCK_RW_FAIR(lockname) \
        rwlock_t lockname=RWLOCK_RW_FAIR(lockname)


    static inline void _RwLockReadLockRdFirst(rwlock_t *lock)
    {
        MutexlockLock(&lock->count_mutex,MUTEX_LOCK_MODE_BLOCK);
        if(!lock->count)
            MutexlockLock(&lock->rw_mutex,MUTEX_LOCK_MODE_BLOCK);
        lock->count++;
        MutexlockUnlock(&lock->count_mutex);
    }

    static inline void _RwLockReadUnlockRdFirst(rwlock_t *lock)
    {
        MutexlockLock(&lock->count_mutex,MUTEX_LOCK_MODE_BLOCK);
        lock->count--;
        if(!lock->count)
        {
            MutexlockUnlock(&lock->rw_mutex);
        }
        MutexlockUnlock(&lock->count_mutex);
    }

    static inline void _RwLockWriteLockRdFirst(rwlock_t *lock)
    {
        MutexlockLock(&lock->rw_mutex,MUTEX_LOCK_MODE_BLOCK);
    }

    static inline void _RwLockWriteUnLockRdFirst(rwlock_t *lock)
    {
        MutexlockUnlock(&lock->rw_mutex);
    }

    static inline void _RwLockReadLockWrFirst(rwlock_t *lock)
    {
        MutexlockLock(&lock->write_mutex,MUTEX_LOCK_MODE_BLOCK);
        MutexlockLock(&lock->count_mutex,MUTEX_LOCK_MODE_BLOCK);
        if(!lock->count)
        {
            MutexlockLock(&lock->rw_mutex,MUTEX_LOCK_MODE_BLOCK);
        }
        lock->count++;
        MutexlockUnlock(&lock->count_mutex);
        MutexlockUnlock(&lock->write_mutex);
    }

    #define _RwLockReadUnLockWrFirst _RwLockReadUnlockRdFirst
    
    static inline void _RwLockWriteLockWrFirst(rwlock_t *lock)
    {
        MutexlockLock(&lock->write_mutex,MUTEX_LOCK_MODE_BLOCK);
        MutexlockLock(&lock->rw_mutex,MUTEX_LOCK_MODE_BLOCK);
    }

    static inline void _RwLockWriteUnLockWrFirst(rwlock_t *lock)
    {
        MutexlockUnlock(&lock->rw_mutex);
        MutexlockUnlock(&lock->write_mutex);
    }

    #define _RwLockReadLockRwFair _RwLockReadLockWrFirst
    #define _RwLockReadUnLockRwFair _RwLockReadUnLockWrFirst


    static inline void _RwLockWriteLockRwFair(rwlock_t *lock)
    {
        MutexlockLock(&lock->write_mutex,MUTEX_LOCK_MODE_BLOCK);
        MutexlockLock(&lock->rw_mutex,MUTEX_LOCK_MODE_BLOCK);
        MutexlockUnlock(&lock->write_mutex);
    }

    static inline void _RwLockWriteUnLockRwFair(rwlock_t *lock)
    {
        MutexlockUnlock(&lock->rw_mutex);
    }

    void RwLockInit(rwlock_t *lock,rwlock_arg_t arg);
    
    static inline void RwLockRdLock(rwlock_t *lock)
    {
        if(lock->read_lock)
            lock->read_lock(lock);
    }

    static inline void RwLockRdUnlock(rwlock_t *lock)
    {
        if(lock->read_unlock)
            lock->read_unlock(lock);
    }

    static inline void RwLockWriteLock(rwlock_t *lock)
    {
        if(lock->write_lock)
            lock->write_lock(lock);
    }

    static inline void RwLockWriteUnLock(rwlock_t *lock)
    {
        if(lock->write_unlock)
            lock->write_unlock(lock);
    }


#endif